﻿namespace GoDiscover.Web
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel.Composition;
	using System.ComponentModel.Composition.Hosting;
	using System.Diagnostics;
	using System.Linq;

	/// <summary>
	/// "Tag" interface for web applications to be auto-registered (exported) with the Managed Extensibility Framework
	/// and also  to be used for generic constraints on web pages.
	/// </summary>
	[InheritedExport]
	public interface IWebApplication
	{
	}

	/// <summary>
	/// Base contract for a web page to be used for generic constraints on web views.
	/// </summary>
	public interface IWebPage
	{
	}

	/// <summary>
	/// "Tag" interface for web pages to be auto-registered (exported) with the Managed Extensibility Framework.
	/// </summary>
	[InheritedExport]
	public interface IWebPage<TApplication> : IWebPage
		where TApplication : IWebApplication
	{
	}

	/// <summary>
	/// Base contract for a web view to be used for generic constraints.
	/// </summary>
	public interface IWebView
	{
	}

	/// <summary>
	/// "Tag" interface for web views to be auto-registered (exported) with the Managed Extensibility Framework.
	/// </summary>
	[InheritedExport]
	public interface IWebView<TPage> : IWebView
		where TPage : IWebPage
	{
	}

	/// <summary>
	/// Base class for web applications that will be auto-registered (exported) with the Managed Extensibility Framework
	/// and auto-discover the web pages associated with the application.
	/// </summary>
	/// <typeparam name="TSelf">The type of the web application.</typeparam>
	public abstract class WebApplication<TSelf> : IWebApplication, IPartImportsSatisfiedNotification
		where TSelf : WebApplication<TSelf>, IWebApplication, IPartImportsSatisfiedNotification
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="WebApplication&lt;TSelf&gt;"/> class.
		/// </summary>
		protected WebApplication()
		{
			this.Pages = new IWebPage<TSelf>[0];
		}

		/// <summary>
		/// Gets or sets the container.
		/// </summary>
		/// <value>
		/// The container.
		/// </value>
		[Import]
		public virtual CompositionContainer Container { get; set; }

		/// <summary>
		/// Gets or sets the pages.
		/// </summary>
		/// <value>
		/// The pages.
		/// </value>
		public virtual IEnumerable<IWebPage<TSelf>> Pages { get; set; }

		/// <summary>
		/// Called when a part's imports have been satisfied and it is safe to use.
		/// </summary>
		public virtual void OnImportsSatisfied()
		{
			Debug.Assert(this.Container != null, "An instance of the MEF CompositionContainer containing the exports for the GoDiscover framework must be registered within the CompositionContainer itself.");

			if (this.Container == null)
			{
				throw new InvalidOperationException("An instance of the MEF CompositionContainer containing the exports for the GoDiscover framework must be registered within the CompositionContainer itself.");
			}

			var pages = this.Container.GetExportedValues<IWebPage<TSelf>>();

			this.Pages = pages;
		}

		/// <summary>
		/// Gets the first page in the collection of pages that matches the specified type.
		/// </summary>
		/// <typeparam name="TPage">The type of the page.</typeparam>
		/// <returns>The page of the specified <typeparamref name="TPage"/> type.</returns>
		public virtual TPage FirstPageOf<TPage>()
		{
			return this.Pages.OfType<TPage>().FirstOrDefault();
		}
	}

	/// <summary>
	/// Base class for web pages that will be auto-registered (exported) with the Managed Extensibility Framework
	/// and auto-discover the web views associated with the web page.
	/// </summary>
	/// <typeparam name="TApplication">The type of the web application associated with this page.</typeparam>
	/// <typeparam name="TSelf">The type of the web page.</typeparam>
	public abstract class WebPage<TApplication, TSelf> : IWebPage<TApplication>, IPartImportsSatisfiedNotification
		where TApplication : IWebApplication
		where TSelf : IWebPage<TApplication>, IPartImportsSatisfiedNotification
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="WebPage&lt;TApplication, TSelf&gt;"/> class.
		/// </summary>
		protected WebPage()
		{
			this.Views = new IWebView<TSelf>[0];
		}

		/// <summary>
		/// Gets or sets the container.
		/// </summary>
		/// <value>
		/// The container.
		/// </value>
		[Import]
		public virtual CompositionContainer Container { get; set; }

		/// <summary>
		/// Gets or sets the views.
		/// </summary>
		/// <value>
		/// The views.
		/// </value>
		public virtual IEnumerable<IWebView<TSelf>> Views { get; set; }

		/// <summary>
		/// Called when a part's imports have been satisfied and it is safe to use.
		/// </summary>
		public virtual void OnImportsSatisfied()
		{
			Debug.Assert(this.Container != null, "An instance of the MEF CompositionContainer containing the exports for the GoDiscover framework must be registered within the CompositionContainer itself.");

			if (this.Container == null)
			{
				throw new InvalidOperationException("An instance of the MEF CompositionContainer containing the exports for the GoDiscover framework must be registered within the CompositionContainer itself.");
			}

			var views = this.Container.GetExportedValues<IWebView<TSelf>>();

			this.Views = views;
		}

		/// <summary>
		/// Gets the first view in the collection of views that matches the specified type.
		/// </summary>
		/// <typeparam name="TView">The type of the view.</typeparam>
		/// <returns>The view of the specified <typeparamref name="TView"/> type.</returns>
		public virtual TView FirstViewOf<TView>()
		{
			return this.Views.OfType<TView>().FirstOrDefault();
		}
	}

	/// <summary>
	/// Base class for web views that will be auto-registered (exported) with the Managed Extensibility Framework.
	/// </summary>
	/// <typeparam name="TPage">The type of the web page associated with this view.</typeparam>
	/// <typeparam name="TSelf">The type of the web view.</typeparam>
	public abstract class WebView<TPage, TSelf> : IWebView<TPage>
		where TPage : IWebPage
		where TSelf : IWebView<TPage>
	{
	}

	/// <summary>
	/// Extensions to discoverable types.
	/// </summary>
	public static class DiscoverableExtensions
	{
		/// <summary>
		/// Gets the first object in the collection that matches the specified type.
		/// </summary>
		/// <typeparam name="TType">The type of the type.</typeparam>
		/// <param name="rootItems">The root items.</param>
		/// <returns>The first object in the collection that matches the specified type.</returns>
		public static TType FirstOf<TType>(this IEnumerable<IWebApplication> applications)
		{
			return applications.OfType<TType>().FirstOrDefault();
		}

		/// <summary>
		/// Gets the first object in the collection that matches the specified type.
		/// </summary>
		/// <typeparam name="TType">The type of the type.</typeparam>
		/// <param name="rootItems">The root items.</param>
		/// <returns>The first object in the collection that matches the specified type.</returns>
		public static TType FirstOf<TType>(this IEnumerable<IWebPage> pages)
		{
			return pages.OfType<TType>().FirstOrDefault();
		}

		/// <summary>
		/// Gets the first object in the collection that matches the specified type.
		/// </summary>
		/// <typeparam name="TType">The type of the type.</typeparam>
		/// <param name="rootItems">The root items.</param>
		/// <returns>The first object in the collection that matches the specified type.</returns>
		public static TType FirstOf<TType>(this IEnumerable<IWebView> views)
		{
			return views.OfType<TType>().FirstOrDefault();
		}
	}
}